import Cesium, { Cartesian3, GroundPrimitive, MaterialAppearance, SampledProperty } from '../../Ces/Cesium'

export type WeTyphoonCircleImagePrimitiveOptions = {
  image: HTMLImageElement
  rotationPerFrame?: number
  rotationFactor: number
}

class WeTyphoonCircleImagePrimitive {
  private dirty: boolean = false
  private image: HTMLImageElement
  private circleGeometryPrimitive: GroundPrimitive
  private appearance: MaterialAppearance

  private rotationSampledProperty: SampledProperty
  private rotationFactor: number

  private _position: Cartesian3
  private _radius: number
  constructor(options: WeTyphoonCircleImagePrimitiveOptions) {
    this.image = options.image
    this.rotationFactor = options.rotationFactor
  }

  get radius() {
    return this._radius
  }

  set radius(value) {
    this._radius = value
    this.dirty = true
  }

  get position() {
    return this._position
  }

  set position(value) {
    this._position = value
    this.dirty = true
  }

  rebuild() {
    const { image, position, radius, rotationFactor } = this
    this.circleGeometryPrimitive?.destroy()

    if (!this.appearance) {
      this.appearance = new Cesium.MaterialAppearance({
        renderState: {
          depthTest: { enabled: false },
          depthMask: true,
          blending: Cesium.BlendingState.ALPHA_BLEND,
          cull: {
            enabled: true,
            face: Cesium.CullFace.BACK,
          },
        },
        translucent: true,
        material: new Cesium.Material({
          fabric: {
            uniforms: {
              image,
              rotation: 0,
            },
            source: `
                    czm_material czm_getMaterial(czm_materialInput materialInput) {
                        czm_material material = czm_getDefaultMaterial(materialInput);
                        vec2 st = materialInput.st;
                        float cosA = cos(rotation);
                        float sinA = sin(rotation);
                        st -= vec2(0.5);
                        mat2 rotMat = mat2(cosA, -sinA, sinA, cosA);
                        st = rotMat * st;
                        st += vec2(0.5);
                        vec4 color = texture(image, st);
                        material.diffuse = color.rgb;
                        material.alpha = color.a;
                        if(material.alpha == 0.0) discard;
                        return material;
                    }
                    `,
          },
        }),
      })

      const property = new Cesium.SampledProperty(Number)
      const now = Cesium.JulianDate.now()
      property.addSample(now, 0.0)
      property.addSample(
        Cesium.JulianDate.addSeconds(now, rotationFactor, new Cesium.JulianDate()),
        Cesium.Math.TWO_PI,
      )
      property.backwardExtrapolationType = Cesium.ExtrapolationType.EXTRAPOLATE
      property.forwardExtrapolationType = Cesium.ExtrapolationType.EXTRAPOLATE

      this.rotationSampledProperty = property

      this.image = undefined
    }

    const appearance = this.appearance

    this.circleGeometryPrimitive = new Cesium.GroundPrimitive({
      geometryInstances: [
        new Cesium.GeometryInstance({
          geometry: new Cesium.CircleGeometry({
            center: position,
            radius,
          }),
        }),
      ],
      asynchronous: false,
      appearance,
    })
  }

  update(frameState) {
    if (!frameState.multiplier) return
    if (this.dirty) {
      this.dirty = false
      this.rebuild()
    }

    if (!this.circleGeometryPrimitive) return

    const rotation = this.rotationSampledProperty.getValue(frameState.time) / frameState.multiplier
    this.circleGeometryPrimitive.appearance.material.uniforms.rotation = rotation % Cesium.Math.TWO_PI
    //@ts-ignore
    this.circleGeometryPrimitive.update(frameState)
  }

  destroy() {
    //@ts-ignore
    this.circleGeometryPrimitive = this.circleGeometryPrimitive && this.circleGeometryPrimitive?.destroy()
    return Cesium.destroyObject(this)
  }
}

export default WeTyphoonCircleImagePrimitive
